home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / couchdb / schema.py < prev    next >
Text File  |  2009-07-02  |  22KB  |  729 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright (C) 2007-2009 Christopher Lenz
  4. # All rights reserved.
  5. #
  6. # This software is licensed as described in the file COPYING, which
  7. # you should have received as part of this distribution.
  8.  
  9. """Mapping from raw JSON data structures to Python objects and vice versa.
  10.  
  11. >>> from couchdb import Server
  12. >>> server = Server('http://localhost:5984/')
  13. >>> db = server.create('python-tests')
  14.  
  15. To define a document schema, you declare a Python class inherited from
  16. `Document`, and add any number of `Field` attributes:
  17.  
  18. >>> class Person(Document):
  19. ...     name = TextField()
  20. ...     age = IntegerField()
  21. ...     added = DateTimeField(default=datetime.now)
  22. >>> person = Person(name='John Doe', age=42)
  23. >>> person.store(db) #doctest: +ELLIPSIS
  24. <Person ...>
  25. >>> person.age
  26. 42
  27.  
  28. You can then load the data from the CouchDB server through your `Document`
  29. subclass, and conveniently access all attributes:
  30.  
  31. >>> person = Person.load(db, person.id)
  32. >>> old_rev = person.rev
  33. >>> person.name
  34. u'John Doe'
  35. >>> person.age
  36. 42
  37. >>> person.added                #doctest: +ELLIPSIS
  38. datetime.datetime(...)
  39.  
  40. To update a document, simply set the attributes, and then call the ``store()``
  41. method:
  42.  
  43. >>> person.name = 'John R. Doe'
  44. >>> person.store(db)            #doctest: +ELLIPSIS
  45. <Person ...>
  46.  
  47. If you retrieve the document from the server again, you should be getting the
  48. updated data:
  49.  
  50. >>> person = Person.load(db, person.id)
  51. >>> person.name
  52. u'John R. Doe'
  53. >>> person.rev != old_rev
  54. True
  55.  
  56. >>> del server['python-tests']
  57. """
  58.  
  59. from calendar import timegm
  60. from datetime import date, datetime, time
  61. from decimal import Decimal
  62. from time import strptime, struct_time
  63.  
  64. from couchdb.design import ViewDefinition
  65.  
  66. __all__ = ['Schema', 'Document', 'Field', 'TextField', 'FloatField',
  67.            'IntegerField', 'LongField', 'BooleanField', 'DecimalField',
  68.            'DateField', 'DateTimeField', 'TimeField', 'DictField', 'ListField']
  69. __docformat__ = 'restructuredtext en'
  70.  
  71. DEFAULT = object()
  72.  
  73.  
  74. class Field(object):
  75.     """Basic unit for mapping a piece of data between Python and JSON.
  76.     
  77.     Instances of this class can be added to subclasses of `Document` to describe
  78.     the schema of a document.
  79.     """
  80.  
  81.     def __init__(self, name=None, default=None):
  82.         self.name = name
  83.         self.default = default
  84.  
  85.     def __get__(self, instance, owner):
  86.         if instance is None:
  87.             return self
  88.         value = instance._data.get(self.name)
  89.         if value is not None:
  90.             value = self._to_python(value)
  91.         elif self.default is not None:
  92.             default = self.default
  93.             if callable(default):
  94.                 default = default()
  95.             value = default
  96.         return value
  97.  
  98.     def __set__(self, instance, value):
  99.         if value is not None:
  100.             value = self._to_json(value)
  101.         instance._data[self.name] = value
  102.  
  103.     def _to_python(self, value):
  104.         return unicode(value)
  105.  
  106.     def _to_json(self, value):
  107.         return self._to_python(value)
  108.  
  109.  
  110. class SchemaMeta(type):
  111.  
  112.     def __new__(cls, name, bases, d):
  113.         fields = {}
  114.         for base in bases:
  115.             if hasattr(base, '_fields'):
  116.                 fields.update(base._fields)
  117.         for attrname, attrval in d.items():
  118.             if isinstance(attrval, Field):
  119.                 if not attrval.name:
  120.                     attrval.name = attrname
  121.                 fields[attrname] = attrval
  122.         d['_fields'] = fields
  123.         return type.__new__(cls, name, bases, d)
  124.  
  125.  
  126. class Schema(object):
  127.     __metaclass__ = SchemaMeta
  128.  
  129.     def __init__(self, **values):
  130.         self._data = {}
  131.         for attrname, field in self._fields.items():
  132.             if attrname in values:
  133.                 setattr(self, attrname, values.pop(attrname))
  134.             else:
  135.                 setattr(self, attrname, getattr(self, attrname))
  136.  
  137.     def __iter__(self):
  138.         return iter(self._data)
  139.  
  140.     def __len__(self):
  141.         return len(self._data or ())
  142.  
  143.     def __delitem__(self, name):
  144.         del self._data[name]
  145.  
  146.     def __getitem__(self, name):
  147.         return self._data[name]
  148.  
  149.     def __setitem__(self, name, value):
  150.         self._data[name] = value
  151.  
  152.     def get(self, name, default):
  153.         return self._data.get(name, default)
  154.  
  155.     def setdefault(self, name, default):
  156.         return sef._data.setdefault(name, default)
  157.  
  158.     def unwrap(self):
  159.         return self._data
  160.  
  161.     @classmethod
  162.     def build(cls, **d):
  163.         fields = {}
  164.         for attrname, attrval in d.items():
  165.             if not attrval.name:
  166.                 attrval.name = attrname
  167.             fields[attrname] = attrval
  168.         d['_fields'] = fields
  169.         return type('AnonymousStruct', (cls,), d)
  170.  
  171.     @classmethod
  172.     def wrap(cls, data):
  173.         instance = cls()
  174.         instance._data = data
  175.         return instance
  176.  
  177.     def _to_python(self, value):
  178.         return self.wrap(value)
  179.  
  180.     def _to_json(self, value):
  181.         return self.unwrap()
  182.  
  183.  
  184. class View(object):
  185.     r"""Descriptor that can be used to bind a view definition to a property of
  186.     a `Document` class.
  187.     
  188.     >>> class Person(Document):
  189.     ...     name = TextField()
  190.     ...     age = IntegerField()
  191.     ...     by_name = View('people', '''\
  192.     ...         function(doc) {
  193.     ...             emit(doc.name, doc);
  194.     ...         }''')
  195.     >>> Person.by_name
  196.     <ViewDefinition '_design/people/_view/by_name'>
  197.     
  198.     >>> print Person.by_name.map_fun
  199.     function(doc) {
  200.         emit(doc.name, doc);
  201.     }
  202.     
  203.     That property can be used as a function, which will execute the view.
  204.     
  205.     >>> from couchdb import Database
  206.     >>> db = Database('http://localhost:5984/python-tests')
  207.     
  208.     >>> Person.by_name(db, count=3)
  209.     <ViewResults <PermanentView '_design/people/_view/by_name'> {'count': 3}>
  210.     
  211.     The results produced by the view are automatically wrapped in the
  212.     `Document` subclass the descriptor is bound to. In this example, it would
  213.     return instances of the `Person` class. But please note that this requires
  214.     the values of the view results to be dictionaries that can be mapped to the
  215.     schema defined by the containing `Document` class. Alternatively, the
  216.     ``include_docs`` query option can be used to inline the actual documents in
  217.     the view results, which will then be used instead of the values.
  218.     
  219.     If you use Python view functions, this class can also be used as a
  220.     decorator:
  221.     
  222.     >>> class Person(Document):
  223.     ...     name = TextField()
  224.     ...     age = IntegerField()
  225.     ...
  226.     ...     @View.define('people')
  227.     ...     def by_name(doc):
  228.     ...         yield doc['name'], doc
  229.     
  230.     >>> Person.by_name
  231.     <ViewDefinition '_design/people/_view/by_name'>
  232.  
  233.     >>> print Person.by_name.map_fun
  234.     def by_name(doc):
  235.         yield doc['name'], doc
  236.     """
  237.  
  238.     def __init__(self, design, map_fun, reduce_fun=None, name=None,
  239.                  language='javascript', wrapper=DEFAULT, **defaults):
  240.         """Initialize the view descriptor.
  241.         
  242.         :param design: the name of the design document
  243.         :param map_fun: the map function code
  244.         :param reduce_fun: the reduce function code (optional)
  245.         :param name: the actual name of the view in the design document, if
  246.                      it differs from the name the descriptor is assigned to
  247.         :param language: the name of the language used
  248.         :param wrapper: an optional callable that should be used to wrap the
  249.                         result rows
  250.         :param defaults: default query string parameters to apply
  251.         """
  252.         self.design = design
  253.         self.name = name
  254.         self.map_fun = map_fun
  255.         self.reduce_fun = reduce_fun
  256.         self.language = language
  257.         self.wrapper = wrapper
  258.         self.defaults = defaults
  259.  
  260.     @classmethod
  261.     def define(cls, design, name=None, language='python', wrapper=DEFAULT,
  262.                **defaults):
  263.         """Factory method for use as a decorator (only suitable for Python
  264.         view code).
  265.         """
  266.         def view_wrapped(fun):
  267.             return cls(design, fun, language=language, wrapper=wrapper,
  268.                        **defaults)
  269.         return view_wrapped
  270.  
  271.     def __get__(self, instance, cls=None):
  272.         if self.wrapper is DEFAULT:
  273.             def wrapper(row):
  274.                 if row.doc is not None:
  275.                     return cls.wrap(row.doc)
  276.                 data = row.value
  277.                 data['_id'] = row.id
  278.                 return cls.wrap(data)
  279.         else:
  280.             wrapper = self.wrapper
  281.         return ViewDefinition(self.design, self.name, self.map_fun,
  282.                               self.reduce_fun, language=self.language,
  283.                               wrapper=wrapper, **self.defaults)
  284.  
  285.  
  286. class DocumentMeta(SchemaMeta):
  287.  
  288.     def __new__(cls, name, bases, d):
  289.         for attrname, attrval in d.items():
  290.             if isinstance(attrval, View):
  291.                 if not attrval.name:
  292.                     attrval.name = attrname
  293.         return SchemaMeta.__new__(cls, name, bases, d)
  294.  
  295.  
  296. class Document(Schema):
  297.     __metaclass__ = DocumentMeta
  298.  
  299.     def __init__(self, id=None, **values):
  300.         Schema.__init__(self, **values)
  301.         if id is not None:
  302.             self.id = id
  303.  
  304.     def __repr__(self):
  305.         return '<%s %r@%r %r>' % (type(self).__name__, self.id, self.rev,
  306.                                   dict([(k, v) for k, v in self._data.items()
  307.                                         if k not in ('_id', '_rev')]))
  308.  
  309.     def _get_id(self):
  310.         if hasattr(self._data, 'id'): # When data is client.Document
  311.             return self._data.id
  312.         return self._data.get('_id')
  313.     def _set_id(self, value):
  314.         if self.id is not None:
  315.             raise AttributeError('id can only be set on new documents')
  316.         self._data['_id'] = value
  317.     id = property(_get_id, _set_id, doc='The document ID')
  318.  
  319.     @property
  320.     def rev(self):
  321.         """The document revision.
  322.         
  323.         :type: basestring
  324.         """
  325.         if hasattr(self._data, 'rev'): # When data is client.Document
  326.             return self._data.rev
  327.         return self._data.get('_rev')
  328.  
  329.     def items(self):
  330.         """Return the fields as a list of ``(name, value)`` tuples.
  331.         
  332.         This method is provided to enable easy conversion to native dictionary
  333.         objects, for example to allow use of `schema.Document` instances with
  334.         `client.Database.update`.
  335.         
  336.         >>> class Post(Document):
  337.         ...     title = TextField()
  338.         ...     author = TextField()
  339.         >>> post = Post(id='foo-bar', title='Foo bar', author='Joe')
  340.         >>> sorted(post.items())
  341.         [('_id', 'foo-bar'), ('author', u'Joe'), ('title', u'Foo bar')]
  342.         
  343.         :return: a list of ``(name, value)`` tuples
  344.         """
  345.         retval = []
  346.         if self.id is not None:
  347.             retval.append(('_id', self.id))
  348.             if self.rev is not None:
  349.                 retval.append(('_rev', self.rev))
  350.         for name, value in self._data.items():
  351.             if name not in ('_id', '_rev'):
  352.                 retval.append((name, value))
  353.         return retval
  354.  
  355.     @classmethod
  356.     def load(cls, db, id):
  357.         """Load a specific document from the given database.
  358.         
  359.         :param db: the `Database` object to retrieve the document from
  360.         :param id: the document ID
  361.         :return: the `Document` instance, or `None` if no document with the
  362.                  given ID was found
  363.         """
  364.         doc = db.get(id)
  365.         if doc is None:
  366.             return None
  367.         return cls.wrap(doc)
  368.  
  369.     def store(self, db):
  370.         """Store the document in the given database."""
  371.         if self.id is None:
  372.             docid = db.create(self._data)
  373.             self._data = db.get(docid)
  374.         else:
  375.             db[self.id] = self._data
  376.         return self
  377.  
  378.     @classmethod
  379.     def query(cls, db, map_fun, reduce_fun, language='javascript', **options):
  380.         """Execute a CouchDB temporary view and map the result values back to
  381.         objects of this schema.
  382.         
  383.         Note that by default, any properties of the document that are not
  384.         included in the values of the view will be treated as if they were
  385.         missing from the document. If you want to load the full document for
  386.         every row, set the ``include_docs`` option to ``True``.
  387.         """
  388.         def _wrapper(row):
  389.             if row.doc is not None:
  390.                 return cls.wrap(row.doc)
  391.             data = row.value
  392.             data['_id'] = row.id
  393.             return cls.wrap(data)
  394.         return db.query(map_fun, reduce_fun=reduce_fun, language=language,
  395.                         wrapper=_wrapper, **options)
  396.  
  397.     @classmethod
  398.     def view(cls, db, viewname, **options):
  399.         """Execute a CouchDB named view and map the result values back to
  400.         objects of this schema.
  401.         
  402.         Note that by default, any properties of the document that are not
  403.         included in the values of the view will be treated as if they were
  404.         missing from the document. If you want to load the full document for
  405.         every row, set the ``include_docs`` option to ``True``.
  406.         """
  407.         def _wrapper(row):
  408.             if row.doc is not None: # include_docs=True
  409.                 return cls.wrap(row.doc)
  410.             data = row.value
  411.             data['_id'] = row.id
  412.             return cls.wrap(data)
  413.         return db.view(viewname, wrapper=_wrapper, **options)
  414.  
  415.  
  416. class TextField(Field):
  417.     """Schema field for string values."""
  418.     _to_python = unicode
  419.  
  420.  
  421. class FloatField(Field):
  422.     """Schema field for float values."""
  423.     _to_python = float
  424.  
  425.  
  426. class IntegerField(Field):
  427.     """Schema field for integer values."""
  428.     _to_python = int
  429.  
  430.  
  431. class LongField(Field):
  432.     """Schema field for long integer values."""
  433.     _to_python = long
  434.  
  435.  
  436. class BooleanField(Field):
  437.     """Schema field for boolean values."""
  438.     _to_python = bool
  439.  
  440.  
  441. class DecimalField(Field):
  442.     """Schema field for decimal values."""
  443.  
  444.     def _to_python(self, value):
  445.         return Decimal(value)
  446.  
  447.     def _to_json(self, value):
  448.         return unicode(value)
  449.  
  450.  
  451. class DateField(Field):
  452.     """Schema field for storing dates.
  453.     
  454.     >>> field = DateField()
  455.     >>> field._to_python('2007-04-01')
  456.     datetime.date(2007, 4, 1)
  457.     >>> field._to_json(date(2007, 4, 1))
  458.     '2007-04-01'
  459.     >>> field._to_json(datetime(2007, 4, 1, 15, 30))
  460.     '2007-04-01'
  461.     """
  462.  
  463.     def _to_python(self, value):
  464.         if isinstance(value, basestring):
  465.             try:
  466.                 value = date(*strptime(value, '%Y-%m-%d')[:3])
  467.             except ValueError, e:
  468.                 raise ValueError('Invalid ISO date %r' % value)
  469.         return value
  470.  
  471.     def _to_json(self, value):
  472.         if isinstance(value, datetime):
  473.             value = value.date()
  474.         return value.isoformat()
  475.  
  476.  
  477. class DateTimeField(Field):
  478.     """Schema field for storing date/time values.
  479.     
  480.     >>> field = DateTimeField()
  481.     >>> field._to_python('2007-04-01T15:30:00Z')
  482.     datetime.datetime(2007, 4, 1, 15, 30)
  483.     >>> field._to_json(datetime(2007, 4, 1, 15, 30, 0, 9876))
  484.     '2007-04-01T15:30:00Z'
  485.     >>> field._to_json(date(2007, 4, 1))
  486.     '2007-04-01T00:00:00Z'
  487.     """
  488.  
  489.     def _to_python(self, value):
  490.         if isinstance(value, basestring):
  491.             try:
  492.                 value = value.split('.', 1)[0] # strip out microseconds
  493.                 value = value.rstrip('Z') # remove timezone separator
  494.                 timestamp = timegm(strptime(value, '%Y-%m-%dT%H:%M:%S'))
  495.                 value = datetime.utcfromtimestamp(timestamp)
  496.             except ValueError, e:
  497.                 raise ValueError('Invalid ISO date/time %r' % value)
  498.         return value
  499.  
  500.     def _to_json(self, value):
  501.         if isinstance(value, struct_time):
  502.             value = datetime.utcfromtimestamp(timegm(value))
  503.         elif not isinstance(value, datetime):
  504.             value = datetime.combine(value, time(0))
  505.         return value.replace(microsecond=0).isoformat() + 'Z'
  506.  
  507.  
  508. class TimeField(Field):
  509.     """Schema field for storing times.
  510.     
  511.     >>> field = TimeField()
  512.     >>> field._to_python('15:30:00')
  513.     datetime.time(15, 30)
  514.     >>> field._to_json(time(15, 30))
  515.     '15:30:00'
  516.     >>> field._to_json(datetime(2007, 4, 1, 15, 30))
  517.     '15:30:00'
  518.     """
  519.  
  520.     def _to_python(self, value):
  521.         if isinstance(value, basestring):
  522.             try:
  523.                 value = value.split('.', 1)[0] # strip out microseconds
  524.                 value = time(*strptime(value, '%H:%M:%S')[3:6])
  525.             except ValueError, e:
  526.                 raise ValueError('Invalid ISO time %r' % value)
  527.         return value
  528.  
  529.     def _to_json(self, value):
  530.         if isinstance(value, datetime):
  531.             value = value.time()
  532.         return value.replace(microsecond=0).isoformat()
  533.  
  534.  
  535. class DictField(Field):
  536.     """Field type for nested dictionaries.
  537.     
  538.     >>> from couchdb import Server
  539.     >>> server = Server('http://localhost:5984/')
  540.     >>> db = server.create('python-tests')
  541.  
  542.     >>> class Post(Document):
  543.     ...     title = TextField()
  544.     ...     content = TextField()
  545.     ...     author = DictField(Schema.build(
  546.     ...         name = TextField(),
  547.     ...         email = TextField()
  548.     ...     ))
  549.     ...     extra = DictField()
  550.  
  551.     >>> post = Post(
  552.     ...     title='Foo bar',
  553.     ...     author=dict(name='John Doe',
  554.     ...                 email='john@doe.com'),
  555.     ...     extra=dict(foo='bar'),
  556.     ... )
  557.     >>> post.store(db) #doctest: +ELLIPSIS
  558.     <Post ...>
  559.     >>> post = Post.load(db, post.id)
  560.     >>> post.author.name
  561.     u'John Doe'
  562.     >>> post.author.email
  563.     u'john@doe.com'
  564.     >>> post.extra
  565.     {'foo': 'bar'}
  566.  
  567.     >>> del server['python-tests']
  568.     """
  569.     def __init__(self, schema=None, name=None, default=None):
  570.         Field.__init__(self, name=name, default=default or {})
  571.         self.schema = schema
  572.  
  573.     def _to_python(self, value):
  574.         if self.schema is None:
  575.             return value
  576.         else:
  577.             return self.schema.wrap(value)
  578.  
  579.     def _to_json(self, value):
  580.         if self.schema is None:
  581.             return value
  582.         if not isinstance(value, Schema):
  583.             value = self.schema(**value)
  584.         return value.unwrap()
  585.  
  586.  
  587. class ListField(Field):
  588.     """Field type for sequences of other fields.
  589.  
  590.     >>> from couchdb import Server
  591.     >>> server = Server('http://localhost:5984/')
  592.     >>> db = server.create('python-tests')
  593.  
  594.     >>> class Post(Document):
  595.     ...     title = TextField()
  596.     ...     content = TextField()
  597.     ...     pubdate = DateTimeField(default=datetime.now)
  598.     ...     comments = ListField(DictField(Schema.build(
  599.     ...         author = TextField(),
  600.     ...         content = TextField(),
  601.     ...         time = DateTimeField()
  602.     ...     )))
  603.  
  604.     >>> post = Post(title='Foo bar')
  605.     >>> post.comments.append(author='myself', content='Bla bla',
  606.     ...                      time=datetime.now())
  607.     >>> len(post.comments)
  608.     1
  609.     >>> post.store(db) #doctest: +ELLIPSIS
  610.     <Post ...>
  611.     >>> post = Post.load(db, post.id)
  612.     >>> comment = post.comments[0]
  613.     >>> comment['author']
  614.     'myself'
  615.     >>> comment['content']
  616.     'Bla bla'
  617.     >>> comment['time'] #doctest: +ELLIPSIS
  618.     '...T...Z'
  619.  
  620.     >>> del server['python-tests']
  621.     """
  622.  
  623.     def __init__(self, field, name=None, default=None):
  624.         Field.__init__(self, name=name, default=default or [])
  625.         if type(field) is type:
  626.             if issubclass(field, Field):
  627.                 field = field()
  628.             elif issubclass(field, Schema):
  629.                 field = DictField(field)
  630.         self.field = field
  631.  
  632.     def _to_python(self, value):
  633.         return self.Proxy(value, self.field)
  634.  
  635.     def _to_json(self, value):
  636.         return [self.field._to_json(item) for item in value]
  637.  
  638.  
  639.     class Proxy(list):
  640.  
  641.         def __init__(self, list, field):
  642.             self.list = list
  643.             self.field = field
  644.  
  645.         def __lt__(self, other):
  646.             return self.list < other
  647.  
  648.         def __le__(self, other):
  649.             return self.list <= other
  650.  
  651.         def __eq__(self, other):
  652.             return self.list == other
  653.  
  654.         def __ne__(self, other):
  655.             return self.list != other
  656.  
  657.         def __gt__(self, other):
  658.             return self.list > other
  659.  
  660.         def __ge__(self, other):
  661.             return self.list >= other
  662.  
  663.         def __repr__(self):
  664.             return repr(self.list)
  665.  
  666.         def __str__(self):
  667.             return str(self.list)
  668.  
  669.         def __unicode__(self):
  670.             return unicode(self.list)
  671.  
  672.         def __delitem__(self, index):
  673.             del self.list[index]
  674.  
  675.         def __getitem__(self, index):
  676.             return self.field._to_python(self.list[index])
  677.  
  678.         def __setitem__(self, index, value):
  679.             self.list[index] = self.field._to_json(item)
  680.  
  681.         def __contains__(self, value):
  682.             for item in self.list:
  683.                 if self.field._to_python(item) == value:
  684.                     return True
  685.             return False
  686.  
  687.         def __iter__(self):
  688.             for index in range(len(self)):
  689.                 yield self[index]
  690.  
  691.         def __len__(self):
  692.             return len(self.list)
  693.  
  694.         def __nonzero__(self):
  695.             return bool(self.list)
  696.  
  697.         def append(self, *args, **kwargs):
  698.             if args or not isinstance(self.field, DictField):
  699.                 if len(args) != 1:
  700.                     raise TypeError('append() takes exactly one argument '
  701.                                     '(%s given)' % len(args))
  702.                 value = args[0]
  703.             else:
  704.                 value = kwargs
  705.             self.list.append(self.field._to_json(value))
  706.  
  707.         def count(self, value):
  708.             return self.list.count(self.field._to_json(value))
  709.  
  710.         def extend(self, list):
  711.             for item in list:
  712.                 self.append(item)
  713.  
  714.         def index(self, value):
  715.             return self.list.index(self.field._to_json(value))
  716.  
  717.         def insert(self, idx, *args, **kwargs):
  718.             if args or not isinstance(self.field, DictField):
  719.                 if len(args) != 1:
  720.                     raise TypeError('insert() takes exactly 2 arguments '
  721.                                     '(%s given)' % len(args))
  722.                 value = args[0]
  723.             else:
  724.                 value = kwargs
  725.             self.list.insert(idx, self.field._to_json(value))
  726.  
  727.         def remove(self, value):
  728.             return self.list.remove(self.field._to_json(value))
  729.